#include "EventMainPage.h"

#include "EventAddDialog.h"
#include "EventEntry.h"
#include "EventNetwork.h"
#include "GroupEntry.h"
#include "ObjectArray.h"

#include <libintl.h>
#define _(String) gettext(String)

struct _EventMainPage
{
	AdwBin parent_instance;
	//
	AdwAlertDialog	    *alert_with_error_dialog;
	AdwOverlaySplitView *split_view;
	//
	GtkBox *sidebar_box;
	GtkBox *content_box;
	//
	AdwDialog    *group_add_dialog;
	AdwEntryRow  *group_name_entry;
	AdwSwitchRow *writable_switch;
	AdwSwitchRow *joinable_switch;
	//
	AdwDialog   *group_join_dialog;
	AdwEntryRow *group_id_entry;
	GtkLabel    *group_join_info;
	//
	ObjectArray *group_array;
	ObjectArray *event_array;
	//
	EventString *joined_group_name;
	euint32	     join_group_id;
};

G_DEFINE_FINAL_TYPE(EventMainPage, event_main_page, ADW_TYPE_BIN)

static void
remove_existed_groups(EventMainPage *self)
{
	GtkWidget *widget;
	if (!self->group_array)
		return;
	object_array_iteration_reset(self->group_array);

	widget = (GtkWidget *)object_array_iteration_get(self->group_array);
	while (widget) {
		gtk_box_remove(self->sidebar_box, widget);
		widget = (GtkWidget *)object_array_iteration_get(
			self->group_array);
	}
	event_object_unref(self->group_array);
	self->group_array = NULL;
	last_group_entry  = NULL;
}

static void
remove_existed_events(EventMainPage *self, bool will_destory_array)
{
	GtkWidget *widget;
	if (!self->event_array)
		return;
	object_array_iteration_reset(self->event_array);

	widget = (GtkWidget *)object_array_iteration_get(self->event_array);
	while (widget) {
		gtk_box_remove(self->content_box, widget);
		widget = (GtkWidget *)object_array_iteration_get(
			self->event_array);
	}
	if (will_destory_array) {
		event_object_unref(self->event_array);
		self->event_array = NULL;
	}
}

void
event_main_page_update_all_content(EventMainPage *self)
{
	ObjectArray *array;
	/* 拉取用户的所有组信息和事件条目 */
	array = event_network_fetch_all_groups(event_network);
	event_main_page_take_group_array(self, array);
	array = event_network_fetch_all_events(event_network);
	event_main_page_take_event_array(self, array);
}

static void
entry_modify_cb(EventMainPage *self, EventEntry *modify_entry)
{
	const char  *event_name, *event_describe;
	time_t	     end_time;
	euint32	     group_id, event_id;
	ResponseType re_type;
	bool	     is_important;

	g_return_if_fail(modify_entry != NULL);
	group_id       = event_entry_get_group_id(modify_entry);
	event_id       = event_entry_get_event_id(modify_entry);
	end_time       = event_entry_get_end_time(modify_entry);
	event_name     = event_entry_get_event_name(modify_entry);
	event_describe = event_entry_get_event_describe(modify_entry);
	is_important   = event_entry_get_is_important(modify_entry);
	//
	re_type = event_network_modify_event(event_network, group_id, event_id,
					     end_time, event_name,
					     event_describe, is_important);
	switch (re_type) {
	case RESPONSE_TYPE_SUCCESS:
		event_main_page_update_all_content(self);
		break;
	case RESPONSE_TYPE_NONE:
		adw_alert_dialog_set_body(
			self->alert_with_error_dialog,
			event_network_get_error_info(event_network));
		adw_dialog_present(ADW_DIALOG(self->alert_with_error_dialog),
				   GTK_WIDGET(self));
		break;
	default:
		adw_alert_dialog_set_body(self->alert_with_error_dialog,
					  _(response_type[re_type]));
		adw_dialog_present(ADW_DIALOG(self->alert_with_error_dialog),
				   GTK_WIDGET(self));
		break;
	}
}

static void
group_quit_cb(EventMainPage *self, GroupEntry *group_entry)
{
	g_return_if_fail(group_entry != NULL);
	gtk_box_remove(self->sidebar_box, GTK_WIDGET(group_entry));
	object_array_remove_object(self->group_array, group_entry);
}

void
event_main_page_take_group_array(EventMainPage *self, ObjectArray *array)
{
	GtkWidget *widget;
	if (!array)
		return;
	if (!self) {
		event_object_unref(array);
		return;
	}
	remove_existed_groups(self);

	object_array_iteration_reset(array);
	widget = (GtkWidget *)object_array_iteration_get(array);
	while (widget) {
		/* 对 GroupEntry 安装信号处理器 */
		g_signal_connect_swapped(widget, "quit",
					 G_CALLBACK(group_quit_cb), self);
		gtk_box_append(self->sidebar_box, g_object_ref(widget));
		widget = (GtkWidget *)object_array_iteration_get(array);
	}
	self->group_array = array;
}

static void
entry_remove_cb(EventMainPage *self, EventEntry *modify_entry)
{
	euint32	     group_id, event_id;
	ResponseType re_type;

	g_return_if_fail(modify_entry != NULL);
	group_id = event_entry_get_group_id(modify_entry);
	event_id = event_entry_get_event_id(modify_entry);
	//
	re_type = event_network_remove_event(event_network, group_id, event_id);
	switch (re_type) {
	case RESPONSE_TYPE_SUCCESS:
		gtk_box_remove(self->content_box, GTK_WIDGET(modify_entry));
		object_array_remove_object(self->event_array, modify_entry);
		break;
	case RESPONSE_TYPE_NONE:
		adw_alert_dialog_set_body(
			self->alert_with_error_dialog,
			event_network_get_error_info(event_network));
		adw_dialog_present(ADW_DIALOG(self->alert_with_error_dialog),
				   GTK_WIDGET(self));
		break;
	default:
		adw_alert_dialog_set_body(self->alert_with_error_dialog,
					  _(response_type[re_type]));
		adw_dialog_present(ADW_DIALOG(self->alert_with_error_dialog),
				   GTK_WIDGET(self));
		break;
	}
}

void
event_main_page_take_event_array(EventMainPage *self, ObjectArray *array)
{
	GtkWidget *widget;
	if (!array)
		return;
	if (!self) {
		event_object_unref(array);
		return;
	}
	remove_existed_events(self, true);

	/* 按时间进行排序 */
	object_array_set_sort_function(
		array, (ObjectSortFunc)event_entry_compare_time);
	object_array_set_is_positive_sequence(array, true);
	object_array_sort(array);

	object_array_iteration_reset(array);
	widget = (GtkWidget *)object_array_iteration_get(array);
	while (widget) {
		/* 对 EventEntry 部件安装信号处理器 */
		g_signal_connect_swapped(widget, "remove-entry",
					 G_CALLBACK(entry_remove_cb), self);
		g_signal_connect_swapped(widget, "entry-modify",
					 G_CALLBACK(entry_modify_cb), self);
		/* 请注意！已对 EventEntry 作了引用处理，从 GtkBox 移除
                   不会导致对象被销毁，对象会继续留存在 ObjectArray 中 */
		gtk_box_append(self->content_box, g_object_ref(widget));
		widget = (GtkWidget *)object_array_iteration_get(array);
	}
	self->event_array = array;
}

void
event_main_page_set_sidebar(EventMainPage *self, bool will_show_sidebar)
{
	g_return_if_fail(self != NULL);
	adw_overlay_split_view_set_show_sidebar(self->split_view,
						will_show_sidebar);
}

ObjectArray *
event_main_page_dup_usable_group_array(EventMainPage *self)
{
	ObjectArray *group_array = NULL;
	GroupEntry  *group_entry;
	g_return_val_if_fail(self != NULL, NULL);

	if (!self->group_array)
		return NULL;
	object_array_iteration_reset(self->group_array);
	group_entry =
		(GroupEntry *)object_array_iteration_get(self->group_array);
	while (group_entry) {
		if (group_entry_verify_can_modify(group_entry)) {
			if (!group_array) {
				group_array = object_array_new(10);
				object_array_set_object_finalize(group_array,
								 NULL);
			}
			object_array_insert_object(group_array, group_entry);
		}
		group_entry = (GroupEntry *)object_array_iteration_get(
			self->group_array);
	}
	return group_array;
}

static void
group_add_cb(EventMainPage *self, gpointer data)
{
	/* 重置新增组界面的状态 */
	gtk_editable_set_text(GTK_EDITABLE(self->group_name_entry), "");
	adw_switch_row_set_active(self->writable_switch, false);
	adw_switch_row_set_active(self->joinable_switch, false);
	adw_dialog_present(self->group_add_dialog, GTK_WIDGET(self));
}

static void
group_join_cb(EventMainPage *self, gpointer data)
{
	adw_dialog_present(self->group_join_dialog, GTK_WIDGET(self));
}

static void
group_add_dialog_cancel_cb(EventMainPage *self, gpointer data)
{
	adw_dialog_close(self->group_add_dialog);
}

static void
group_join_dialog_cancel_cb(EventMainPage *self, gpointer data)
{
	adw_dialog_close(self->group_join_dialog);
}

static ResponseType
user_join_group(EventMainPage *self)
{
	EventString *group_name;
	ResponseType re_type;

	group_name =
		event_network_join_group(event_network, self->join_group_id);
	re_type = event_network_get_response_type(event_network);
	event_main_page_take_joined_group_name(self, group_name);
	if (re_type == RESPONSE_TYPE_NONE) {
		adw_alert_dialog_set_body(
			self->alert_with_error_dialog,
			event_network_get_error_info(event_network));
		adw_dialog_present(ADW_DIALOG(self->alert_with_error_dialog),
				   GTK_WIDGET(self));
	} else if (re_type == RESPONSE_TYPE_SUCCESS) {
		event_main_page_update_all_content(self);
	}
	return re_type;
}

/* 按下加入事件组的确认按钮 */
static void
group_join_apply_cb(EventMainPage *self, gpointer data)
{
	ResponseType re_type;
	const char  *group_id_str;
	int	     valid;

	group_id_str =
		gtk_editable_get_text(GTK_EDITABLE(self->group_id_entry));
	for (valid = 0; valid < 7 && group_id_str[valid] != '\0'; ++valid)
		if (group_id_str[valid] < '0' || group_id_str[valid] > '9')
			break;
	if (valid != 7) {
		gtk_label_set_label(
			self->group_join_info,
			_("Please check if seven digits are entered."));
		return;
	}
	sscanf(group_id_str, "%u", &self->join_group_id);

	re_type = user_join_group(self);
	switch (re_type) {
	case RESPONSE_TYPE_NONE:
		adw_dialog_close(self->group_join_dialog);
		break;
	case RESPONSE_TYPE_SUCCESS:
		adw_dialog_close(self->group_join_dialog);
		break;
	default:
		gtk_label_set_label(self->group_join_info,
				    _(response_type[re_type]));
		break;
	}

	if (self->joined_group_name) {
		event_object_unref(self->joined_group_name);
		self->joined_group_name = NULL;
	}
}

static void
group_name_entry_change_cb(EventMainPage *self, gpointer data)
{
	const char *css = "entry-input-error";

	if (!GPOINTER_TO_INT(g_object_get_data(G_OBJECT(self->group_name_entry),
					       "is-error-entry"))) {
		return;
	}
	gtk_widget_remove_css_class(GTK_WIDGET(self->group_name_entry), css);
	g_object_set_data(G_OBJECT(self->group_name_entry), "is-error-entry",
			  GINT_TO_POINTER(0));
}

/* 新增组按下按钮触发的信号 */
static void
group_add_dialog_apply_cb(EventMainPage *self, gpointer data)
{
	EventString *group_name;
	GroupEntry  *group_entry;
	ResponseType re_type;
	euint32	     new_group_id;
	bool	     writable, joinable;
	const char  *_group_name, *css[] = { "entry-input-error", NULL };

	_group_name =
		gtk_editable_get_text(GTK_EDITABLE(self->group_name_entry));
	if (strlen(_group_name) == 0) {
		g_object_set_data(G_OBJECT(self->group_name_entry),
				  "is-error-entry", GINT_TO_POINTER(1));
		gtk_widget_set_css_classes(GTK_WIDGET(self->group_name_entry),
					   css);
		return;
	}

	group_name   = event_string_new(event_main_page_get_group_name(self));
	writable     = event_main_page_get_group_writable(self);
	joinable     = event_main_page_get_group_joinable(self);
	new_group_id = event_network_add_new_group(
		event_network, ES_GET(group_name), writable, joinable);
	/* 处理网络返回状态 */
	re_type = event_network_get_response_type(event_network);
	switch (re_type) {
	case RESPONSE_TYPE_SUCCESS:
		group_entry = GROUP_ENTRY(group_entry_new());
		group_entry_set_group_id(group_entry, new_group_id);
		group_entry_set_group_name(group_entry, ES_GET(group_name));
		group_entry_set_is_creator(group_entry, true);
		event_main_page_add_group_to_sidebar(self, group_entry);
		break;
	case RESPONSE_TYPE_NONE:
		adw_alert_dialog_set_body(
			self->alert_with_error_dialog,
			event_network_get_error_info(event_network));
		adw_dialog_present(ADW_DIALOG(self->alert_with_error_dialog),
				   GTK_WIDGET(self));
		break;
	default:
		adw_alert_dialog_set_body(self->alert_with_error_dialog,
					  _(response_type[re_type]));
		adw_dialog_present(ADW_DIALOG(self->alert_with_error_dialog),
				   GTK_WIDGET(self));
		break;
	}
	event_object_unref(group_name);
	/* 关闭窗口 */
	adw_dialog_close(self->group_add_dialog);
}

const char *
event_main_page_get_group_name(EventMainPage *self)
{
	g_return_val_if_fail(self != NULL, NULL);
	return gtk_editable_get_text(GTK_EDITABLE(self->group_name_entry));
}

bool
event_main_page_get_group_writable(EventMainPage *self)
{
	g_return_val_if_fail(self != NULL, false);
	return (bool)adw_switch_row_get_active(self->writable_switch);
}

bool
event_main_page_get_group_joinable(EventMainPage *self)
{
	g_return_val_if_fail(self != NULL, false);
	return (bool)adw_switch_row_get_active(self->joinable_switch);
}

void
event_main_page_add_group_to_sidebar(EventMainPage *self,
				     GroupEntry	   *group_entry)
{
	if (!self) {
		if (group_entry)
			g_object_unref(group_entry);
	}
	g_return_if_fail(self != NULL || group_entry != NULL);
	if (!self->group_array) {
		self->group_array = object_array_new(10);
		object_array_set_object_finalize(self->group_array,
						 g_object_unref);
	}
	object_array_insert_object(self->group_array, group_entry);

	gtk_box_append(self->sidebar_box, GTK_WIDGET(group_entry));
}

void
event_main_page_add_event_to_content(EventMainPage *self,
				     EventEntry	   *event_entry)
{
	GtkWidget *widget;
	if (!self) {
		if (event_entry)
			g_object_unref(event_entry);
	}
	g_return_if_fail(self != NULL || event_entry != NULL);

	/* 对 EventEntry 部件安装信号处理器 */
	g_signal_connect_swapped(event_entry, "entry-modify",
				 G_CALLBACK(entry_modify_cb), self);
	g_signal_connect_swapped(event_entry, "remove-entry",
				 G_CALLBACK(entry_remove_cb), self);
	if (!self->event_array) {
		self->event_array = object_array_new(100);
		object_array_set_object_finalize(self->event_array,
						 g_object_unref);
		object_array_set_sort_function(
			self->event_array,
			(ObjectSortFunc)event_entry_compare_time);
		object_array_set_is_positive_sequence(self->event_array, true);
	}
	remove_existed_events(self, false);
	object_array_insert_object(self->event_array, event_entry);
	object_array_sort(self->event_array);

	object_array_iteration_reset(self->event_array);
	widget = (GtkWidget *)object_array_iteration_get(self->event_array);
	while (widget) {
		gtk_box_append(self->content_box, g_object_ref(widget));
		widget = (GtkWidget *)object_array_iteration_get(
			self->event_array);
	}
}

void
event_main_page_take_joined_group_name(EventMainPage *self,
				       EventString   *group_name)
{
	g_return_if_fail(self != NULL);
	if (self->joined_group_name)
		event_object_unref(self->joined_group_name);
	self->joined_group_name = group_name;
}

/* 添加新事件 */
static void
add_new_event_cb(EventMainPage *self, EventAddDialog *dialog)
{
	EventEntry  *event_entry;
	const char  *event_name, *event_describe, *group_name;
	time_t	     end_time;
	euint32	     group_id, new_event_id;
	ResponseType re_type;
	bool	     is_important;

	event_name     = event_add_dialog_get_event_name(dialog);
	event_describe = event_add_dialog_get_event_describe(dialog);
	end_time       = event_add_dialog_get_end_time(dialog);
	group_id       = event_add_dialog_get_group_id(dialog);
	is_important   = event_add_dialog_get_is_important(dialog);
	group_name     = event_add_dialog_get_group_name(dialog);
	//
	new_event_id = event_network_add_new_event(event_network, group_id,
						   end_time, event_name,
						   event_describe,
						   is_important);
	//
	re_type = event_network_get_response_type(event_network);
	switch (re_type) {
	case RESPONSE_TYPE_SUCCESS:
		event_entry = EVENT_ENTRY(event_entry_new());
		event_entry_set_can_modify(event_entry, true);
		event_entry_set_end_time(event_entry, end_time);
		event_entry_set_event_describe(event_entry, event_describe);
		event_entry_set_event_id(event_entry, new_event_id);
		event_entry_set_event_name(event_entry, event_name);
		event_entry_set_group_id(event_entry, group_id);
		event_entry_set_group_name(event_entry, group_name);
		event_entry_set_is_important(event_entry, is_important);
		event_main_page_add_event_to_content(self, event_entry);
		break;
	case RESPONSE_TYPE_NONE:
		adw_alert_dialog_set_body(
			self->alert_with_error_dialog,
			event_network_get_error_info(event_network));
		adw_dialog_present(ADW_DIALOG(self->alert_with_error_dialog),
				   GTK_WIDGET(self));
		break;
	default:
		adw_alert_dialog_set_body(self->alert_with_error_dialog,
					  _(response_type[re_type]));
		adw_dialog_present(ADW_DIALOG(self->alert_with_error_dialog),
				   GTK_WIDGET(self));
		break;
	}
}

void
event_main_page_create_new_event(EventMainPage *self)
{
	ObjectArray    *usable_group_array;
	EventAddDialog *dialog;

	dialog = event_add_dialog_new();
	g_signal_connect_swapped(dialog, "new-event",
				 G_CALLBACK(add_new_event_cb), self);
	usable_group_array = event_main_page_dup_usable_group_array(self);
	event_add_dialog_take_usable_group(dialog, usable_group_array);
	adw_dialog_present(ADW_DIALOG(dialog), GTK_WIDGET(self));
}

static void
alert_error_close_cb(AdwAlertDialog *self, gchar *response, gpointer user_data)
{
	adw_dialog_close(ADW_DIALOG(self));
}

static void
event_main_page_finalize(GObject *object)
{
	EventMainPage *self = EVENT_MAIN_PAGE(object);
	event_object_unref(self->joined_group_name);
	G_OBJECT_CLASS(event_main_page_parent_class)->finalize(object);
}

static void
event_main_page_class_init(EventMainPageClass *klass)
{
	GObjectClass   *object_class = G_OBJECT_CLASS(klass);
	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);

	gtk_widget_class_set_template_from_resource(
		widget_class, "/org/korack/event/ui/EventMainPage.ui");
	gtk_widget_class_bind_template_child(widget_class, EventMainPage,
					     split_view);
	gtk_widget_class_bind_template_child(widget_class, EventMainPage,
					     sidebar_box);
	gtk_widget_class_bind_template_child(widget_class, EventMainPage,
					     content_box);
	gtk_widget_class_bind_template_child(widget_class, EventMainPage,
					     group_add_dialog);
	gtk_widget_class_bind_template_child(widget_class, EventMainPage,
					     group_name_entry);
	gtk_widget_class_bind_template_child(widget_class, EventMainPage,
					     writable_switch);
	gtk_widget_class_bind_template_child(widget_class, EventMainPage,
					     joinable_switch);
	gtk_widget_class_bind_template_child(widget_class, EventMainPage,
					     group_join_dialog);
	gtk_widget_class_bind_template_child(widget_class, EventMainPage,
					     group_id_entry);
	gtk_widget_class_bind_template_child(widget_class, EventMainPage,
					     group_join_info);
	gtk_widget_class_bind_template_child(widget_class, EventMainPage,
					     alert_with_error_dialog);

	gtk_widget_class_bind_template_callback(widget_class, group_add_cb);
	gtk_widget_class_bind_template_callback(widget_class, group_join_cb);
	gtk_widget_class_bind_template_callback(widget_class,
						group_add_dialog_cancel_cb);
	gtk_widget_class_bind_template_callback(widget_class,
						group_join_dialog_cancel_cb);
	gtk_widget_class_bind_template_callback(widget_class,
						group_add_dialog_apply_cb);
	gtk_widget_class_bind_template_callback(widget_class,
						group_name_entry_change_cb);
	gtk_widget_class_bind_template_callback(widget_class,
						group_join_apply_cb);
	gtk_widget_class_bind_template_callback(widget_class,
						alert_error_close_cb);

	object_class->finalize = event_main_page_finalize;
}

static void
event_main_page_init(EventMainPage *self)
{
	gtk_widget_init_template(GTK_WIDGET(self));

	g_object_set_data(G_OBJECT(self->group_name_entry), "is-error-entry",
			  GINT_TO_POINTER(0));
	//
	self->group_array	= NULL;
	self->event_array	= NULL;
	self->joined_group_name = NULL;
}
